use super::*;

/// Thread-safe function wrapper for N-API
pub struct TsFn(sys::napi_threadsafe_function);

impl Drop for TsFn {
	fn drop(&mut self) {
		unsafe { sys::napi_release_threadsafe_function(self.0, sys::napi_tsfn_release) }
			.result("napi_release_threadsafe_function")
			.map_err(|e| log::error!("{}", e))
			.ok();
	}
}
impl Clone for TsFn {
	fn clone(&self) -> Self {
		unsafe { sys::napi_acquire_threadsafe_function(self.0) } //
			.result("napi_acquire_threadsafe_function")
			.map_err(|e| log::error!("{}", e))
			.ok();
		Self(self.0)
	}
}
impl TsFn {
	/// Creates a new thread-safe function
	pub fn new(env: &Env, func: TsOption<'_>) -> Result<'static, Self> {
		let mut ret = core::ptr::null_mut();
		unsafe {
			sys::napi_create_threadsafe_function(
				env.raw(), //
				func.func.map(|f| f.v().raw()).unwrap_or(core::ptr::null_mut()),
				core::ptr::null_mut(),
				core::ptr::null_mut(),
				func.max_queue_size,
				func.initial_thread_count,
				func.thread_finalize_data,
				func.thread_finalize_cb,
				func.context,
				func.rust,
				&mut ret,
			)
		}
		.result("napi_create_threadsafe_function")?;
		Ok(TsFn(ret))
	}
	/// Gets the context associated with the thread-safe function
	pub fn context(&self) -> Result<'static, *mut core::ffi::c_void> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_threadsafe_function_context(self.0, &mut ret) } //
			.result("napi_get_threadsafe_function_context")?;
		Ok(ret)
	}
	/// Acquires a guard for the thread-safe function
	pub fn acquire(&self) -> Result<'static, TsFnGuard> {
		unsafe { sys::napi_acquire_threadsafe_function(self.0) } //
			.result("napi_acquire_threadsafe_function")?;
		Ok(TsFnGuard(self.0))
	}
	/// References the thread-safe function
	pub fn r#ref(&self, env: &Env) -> Result<'static, ()> {
		unsafe { sys::napi_ref_threadsafe_function(env.raw(), self.0) } //
			.result("napi_ref_threadsafe_function")?;
		Ok(())
	}
	/// Unreferences the thread-safe function
	pub fn unref(&self, env: &Env) -> Result<'static, ()> {
		unsafe { sys::napi_unref_threadsafe_function(env.raw(), self.0) } //
			.result("napi_unref_threadsafe_function")?;
		Ok(())
	}
}

/// Guard for thread-safe function operations
pub struct TsFnGuard(sys::napi_threadsafe_function);
unsafe impl Send for TsFnGuard {}
unsafe impl Sync for TsFnGuard {}
impl Drop for TsFnGuard {
	fn drop(&mut self) {
		unsafe { sys::napi_release_threadsafe_function(self.0, sys::napi_tsfn_release) }
			.result("napi_release_threadsafe_function")
			.map_err(|e| log::error!("{}", e))
			.ok();
	}
}
impl Clone for TsFnGuard {
	fn clone(&self) -> Self {
		unsafe { sys::napi_acquire_threadsafe_function(self.0) } //
			.result("napi_acquire_threadsafe_function")
			.map_err(|e| log::error!("{:?}", e))
			.ok();
		TsFnGuard(self.0)
	}
}

impl TsFnGuard {
	/// Gets the context associated with the thread-safe function
	pub fn context(&self) -> Result<'static, *mut core::ffi::c_void> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_threadsafe_function_context(self.0, &mut ret) } //
			.result("napi_get_threadsafe_function_context")?;
		Ok(ret)
	}
	/// Calls the thread-safe function with blocking behavior
	pub fn call_blocking(&self, data: *mut core::ffi::c_void) -> Result<'static, ()> {
		unsafe { sys::napi_call_threadsafe_function(self.0, data, sys::napi_tsfn_blocking) } //
			.result("napi_call_threadsafe_function")?;
		Ok(())
	}
	/// Calls the thread-safe function with non-blocking behavior
	pub fn call_nonblocking(&self, data: *mut core::ffi::c_void) -> Result<'static, ()> {
		unsafe { sys::napi_call_threadsafe_function(self.0, data, sys::napi_tsfn_nonblocking) } //
			.result("napi_call_threadsafe_function")?;
		Ok(())
	}
	/// Calls the thread-safe function with priority
	pub fn call_priority(
		&self,
		data: *mut core::ffi::c_void,
		priority: sys::napi_task_priority,
		insert_at_front: bool,
	) -> Result<'static, ()> {
		unsafe { sys::napi_call_threadsafe_function_with_priority(self.0, data, priority, insert_at_front) } //
			.result("napi_call_threadsafe_function_with_priority")?;
		Ok(())
	}
	/// Calls the thread-safe function with immediate priority
	pub fn call_immediate(&self, data: *mut core::ffi::c_void, insert_at_front: bool) -> Result<'static, ()> {
		self.call_priority(data, sys::napi_priority_immediate, insert_at_front)
	}
	/// Calls the thread-safe function with high priority
	pub fn call_high(&self, data: *mut core::ffi::c_void, insert_at_front: bool) -> Result<'static, ()> {
		self.call_priority(data, sys::napi_priority_high, insert_at_front)
	}
	/// Calls the thread-safe function with low priority
	pub fn call_low(&self, data: *mut core::ffi::c_void, insert_at_front: bool) -> Result<'static, ()> {
		self.call_priority(data, sys::napi_priority_low, insert_at_front)
	}
	/// Calls the thread-safe function with idle priority
	pub fn call_idle(&self, data: *mut core::ffi::c_void, insert_at_front: bool) -> Result<'static, ()> {
		self.call_priority(data, sys::napi_priority_idle, insert_at_front)
	}
}

/// Options for creating a thread-safe function
pub struct TsOption<'val> {
	pub func: Option<Function<'val>>,
	pub max_queue_size: usize,
	pub initial_thread_count: usize,
	pub thread_finalize_cb: sys::napi_finalize,
	pub thread_finalize_data: *mut core::ffi::c_void,
	pub context: *mut core::ffi::c_void,
	pub rust: sys::napi_threadsafe_function_call_js,
}
impl<'val> Default for TsOption<'val> {
	fn default() -> Self {
		Self {
			func: None,
			max_queue_size: 0,
			initial_thread_count: 1,
			thread_finalize_cb: None,
			thread_finalize_data: core::ptr::null_mut(),
			context: core::ptr::null_mut(),
			rust: None,
		}
	}
}
impl<'val> TsOption<'val> {
	/// Sets the JavaScript function for the thread-safe function
	pub fn function(self, func: Option<Function<'val>>) -> Self {
		Self { func, rust: None, ..self }
	}
	/// Sets the maximum queue size
	pub fn max_queue_size(self, max_queue_size: usize) -> Self {
		Self { max_queue_size, ..self }
	}
	/// Sets the initial thread count
	pub fn initial_thread_count(self, initial_thread_count: usize) -> Self {
		Self {
			initial_thread_count: initial_thread_count.max(1),
			..self
		}
	}
	/// Sets the finalize callback and data
	pub fn finalize(self, thread_finalize_cb: sys::napi_finalize, thread_finalize_data: *mut core::ffi::c_void) -> Self {
		Self {
			thread_finalize_cb,
			thread_finalize_data,
			..self
		}
	}
	/// Sets the context for the thread-safe function
	pub fn context(self, context: *mut core::ffi::c_void) -> Self {
		Self { context, ..self }
	}
	/// Sets the Rust callback function
	pub fn rust(self, rust: sys::napi_threadsafe_function_call_js) -> Self {
		Self { rust, func: None, ..self }
	}
}